using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Text;

// string encoding complete

namespace csammisrun.OscarLib.Utility
{
  /// <summary>
  /// Provides static methods for processing messages in SNAC family 0x0010 -- Server-side buddy icons
  /// </summary>
  internal static class SNAC10
  {
    /// <summary>
    /// Uploads the client's own icon -- SNAC(10,02)
    /// </summary>
    /// <param name="sess">A <see cref="Session"/> object</param>
    public static void UploadIcon(Session sess)
    {
      string filename = sess.UploadIconFilename;
      if (filename == "")  // possibly this means we should remove the SSI item
      {
        return;
      }

      StreamReader sr = null;
      int index = 0;
      byte[] buffer = null;
      try
      {
        sr = new StreamReader(filename);
        buffer = new byte[4 + sr.BaseStream.Length];
        Marshal.InsertUshort(buffer, 0x0001, ref index);
        Marshal.InsertUshort(buffer, (ushort)sr.BaseStream.Length, ref index);

        while (index < buffer.Length)
          index += sr.BaseStream.Read(
            buffer, index, buffer.Length - index);
      }
      catch (Exception)
      {
        sess.OnBuddyIconUploadFailed("Couldn't read " + filename);
        return;
      }
      finally
      {
        if (sr != null)
          sr.Close();
      }

      SNACHeader sh = new SNACHeader();
      sh.FamilyServiceID = (ushort)SNACFamily.SSBIService;
      sh.FamilySubtypeID = (ushort)SSBIService.UploadIconRequest;
      sh.Flags = 0x0000;
      sh.RequestID = sess.GetNextRequestID();

      DataPacket dp = new DataPacket();
      dp.ParentSession = sess;
      dp.SNAC = sh;
      dp.Data = buffer;
      SNACFunctions.BuildFLAP(dp);
    }

    /// <summary>
    /// Processes a response to the client's icon upload -- SNAC(10,03)
    /// </summary>
    /// <param name="dp">A <see cref="DataPacket"/> object</param>
    public static void ProcessUploadIconResponse(DataPacket dp)
    {
      if (dp.Data.Length == 0x0015)
      {
        dp.ParentSession.OnBuddyIconUploadCompleted();
      }
      else
      {
        // The only time I've ever actually seen this happen is when the
        // data was out of range for the buddy icon servers, but IconVerifier
        // should take care of that instance.
        dp.ParentSession.OnBuddyIconUploadFailed(
          "Uploaded data did not hash correctly");
      }
    }

    /// <summary>
    /// Requests a remote client's icon for download -- SNAC(10,04)
    /// </summary>
    /// <param name="sess">A <see cref="Session"/> object</param>
    /// <param name="screenname">The screenname of the remote client</param>
    /// <param name="icon">The <see cref="IconInfo"/> received in SNAC(03,0B)</param>
    public static void RequestIcon(Session sess, string screenname, IconInfo icon)
    {
      SNACHeader sh = new SNACHeader();
      sh.FamilyServiceID = (ushort)SNACFamily.SSBIService;
      sh.FamilySubtypeID = (ushort)SSBIService.AIMBuddyIconRequest;
      sh.Flags = 0x0000;

      Encoding enc = Marshal.ASCII;
      byte screennamelength = (byte)enc.GetByteCount(screenname);

      DataPacket dp = new DataPacket();
      dp.ParentSession = sess;
      dp.SNAC = sh;

      int buffersize = 1 + screennamelength + 4 + 1 + icon.Hash.Length;
      dp.Data = new byte[buffersize];
      int index = 0;

      dp.Data[index++] = screennamelength;
      Marshal.InsertString(dp.Data, screenname, enc, ref index);
      dp.Data[index++] = 0x01;
      Marshal.InsertUshort(dp.Data, icon.ID, ref index);
      dp.Data[index++] = icon.HashType;
      dp.Data[index++] = (byte)icon.Hash.Length;
      Marshal.CopyArray(icon.Hash, dp.Data, 0, ref index);

      SNACFunctions.BuildFLAP(dp);
    }

    /// <summary>
    /// Processes a client's buddy icon sent by the server -- SNAC(10,05)
    /// </summary>
    /// <param name="dp">A <see cref="DataPacket"/> object</param>
    public static void ProcessIconDownload(DataPacket dp)
    {
      int index = 0;

      // Get all of the data out of the data packet
      byte sn_length, checksumtype, cs_length;
      string screenname;
      ushort flags, ic_length;
      byte[] hash; byte[] icon;

      sn_length = dp.Data[index++];
      screenname = Marshal.ByteArrayToString(dp.Data, ref index, sn_length, Marshal.ASCII);
      flags = Marshal.ByteArrayToUshort(dp.Data, ref index);
      checksumtype = dp.Data[index++];
      cs_length = dp.Data[index++];
      hash = new byte[cs_length]; Marshal.CopyArray(dp.Data, hash, ref index);
      ic_length = Marshal.ByteArrayToUshort(dp.Data, ref index);
      icon = new byte[ic_length]; Marshal.CopyArray(dp.Data, icon, ref index);

      // Check the downloaded icon's checksum
      System.Security.Cryptography.MD5CryptoServiceProvider md5 =
        new MD5CryptoServiceProvider();
      byte[] realhash = md5.ComputeHash(icon);
      for (int i = 0; i < realhash.Length; i++)
      {
        if (realhash[i] != hash[i])
        {
          dp.ParentSession.OnBuddyIconDownloadFailed(screenname, "Mismatched MD5 checksums");
          return;
        }
      }

      dp.ParentSession.OnBuddyIconDownloadCompleted(screenname, icon);
    }
  }
}
